package middleware

import (
	"context"
	"encoding/json"
	"net/http"
	"strings"

	"gitee.com/kessil/chicory/internal/config"
	"gitee.com/kessil/chicory/internal/models"
	"gitee.com/kessil/chicory/internal/utils"

	"github.com/golang-jwt/jwt/v4"
)

const (
	UserKey contextKey = "claims"
)

func JWTAuthMiddleware(cfg *config.Config) func(http.Handler) http.Handler {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			authHeader := r.Header.Get("Authorization")
			if authHeader == "" {
				w.Header().Set("Content-Type", "application/json")
				w.WriteHeader(http.StatusUnauthorized)
				json.NewEncoder(w).Encode(models.Error(http.StatusUnauthorized, "Authorization header required"))
				utils.Warn("Authorization header missing")
				return
			}

			tokenString := strings.TrimPrefix(authHeader, "Bearer ")
			if tokenString == authHeader {
				w.Header().Set("Content-Type", "application/json")
				w.WriteHeader(http.StatusUnauthorized)
				json.NewEncoder(w).Encode(models.Error(http.StatusUnauthorized, "Bearer token required"))
				return
			}

			token, err := jwt.ParseWithClaims(tokenString, &models.Claims{}, func(token *jwt.Token) (interface{}, error) {
				return []byte(cfg.JWT.Secret), nil
			})

			if err != nil || !token.Valid {
				w.Header().Set("Content-Type", "application/json")
				w.WriteHeader(http.StatusUnauthorized)
				json.NewEncoder(w).Encode(models.Error(http.StatusUnauthorized, "Invalid token"))
				return
			}

			if claims, ok := token.Claims.(*models.Claims); ok && token.Valid {
				ctx := context.WithValue(r.Context(), UserKey, claims)
				next.ServeHTTP(w, r.WithContext(ctx))
			} else {
				w.Header().Set("Content-Type", "application/json")
				w.WriteHeader(http.StatusUnauthorized)
				json.NewEncoder(w).Encode(models.Error(http.StatusUnauthorized, "Invalid token claims"))
				return
			}
		})
	}
}

// GetClaimsFromContext 从上下文中获取Claims
func GetClaimsFromContext(r *http.Request, paramName string) (*models.Claims, bool) {
	if claims := r.Context().Value(UserKey); claims != nil {
		if c, ok := claims.(*models.Claims); ok {
			return c, true
		}
	}
	return nil, false
}
